/* start observing*/
package Observing;
import java.nio.file.*;
import static java.nio.file.StandardWatchEventKind.*;
import static java.nio.file.LinkOption.*;
import java.nio.file.attribute.*;
import java.io.*;
import java.util.*;

import Algo.Algo;
import DataBase.MenuTag_File;
import DirectsDB.MenuDirects;


public class ObserverThread extends Thread {
	
	private boolean exit;
	public void die(){
		exit=true;
	}

	public class WatchDir {
	    private final WatchService watcher;
	    private final Map<WatchKey,Path> keys;
	    private final boolean recursive;
	    private boolean trace = false;
	    private WatchDirUpdate update;    
	    @SuppressWarnings("unchecked") <T> WatchEvent<T> cast(WatchEvent<?> event) {
	        return (WatchEvent<T>)event;
	    }
	    /**
	     * Register the given directory with the WatchService
	     */
	    public void register(Path dir) throws IOException {
	        WatchKey key = dir.register(watcher, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY);
	        if (trace) {
	            FileRef prev = keys.get(key);
	            if (prev == null) {
	            	//////////new Log("Start watching "+dir); /// ----------------------------------------------
	              //////////  System.out.format("WatchDir: register: %s\n", dir);
	            } else {
	                if (!dir.equals(prev)) {
	                	//log.setLog("update: "+prev+" -> "+dir);
	                   ///////// System.out.format("the sub directory change: %s -> %s\n", prev, dir);
	                }
	            }
	        }
	        keys.put(key, dir);
	    }
	    /**
	     * Register the given directory, and all its sub-directories, with the
	     * WatchService.
	     */
	    private void registerAll(final Path start) throws IOException {
	        // register directory and sub-directories
	        Files.walkFileTree(start, new SimpleFileVisitor<Path>() {
	            @Override
	            public FileVisitResult preVisitDirectory(Path dir) {
	                try {
	                    register(dir);
	                } catch (IOException x) {
	                    throw new IOError(x);
	                }
	                return FileVisitResult.CONTINUE;
	            }
	        });
	    }
	    /**
	     * Creates a WatchService and registers the given directory
	     */
	    public WatchDir(Path dir, boolean recursive) throws IOException {
	        update=new WatchDirUpdate(algoList,dataBase);
	    	this.watcher = FileSystems.getDefault().newWatchService();
	        this.keys = new HashMap<WatchKey,Path>();
	        this.recursive = recursive;
	        if (recursive) {
	         /////////////   System.out.format("Scanning %s ...\n", dir);
	            registerAll(dir);
	           ///////////////// System.out.println("Done.");
	        } else {
	            register(dir);
	        }
	        // enable trace after initial registration
	        this.trace = true;
	    }
	    /**
	     * Process all events for keys queued to the watcher
	     */
	    void processEvents() {
	        while (!exit) {
	            // wait for key to be signalled
	            WatchKey key;
	            try {
	                key = watcher.take();
	            } catch (InterruptedException x) {
	                return;
	            }
	            Path dir = keys.get(key);
	            if (dir == null) {
	                System.err.println("WatchKey not recognized!!");
	                continue;
	            }
	            for (WatchEvent<?> event: key.pollEvents()) {
	                WatchEvent.Kind kind = event.kind();
	                // TBD - provide example of how OVERFLOW event is handled
	                if (kind == OVERFLOW) {
	                    continue;
	                }
	                // Context for directory entry event is the file name of entry
	                WatchEvent<Path> ev = cast(event);
	                Path name = ev.context();
	                Path child = dir.resolve(name);
	                // print out event
	               ////////////////// new Log(""+event.kind().name()+": "+child);///--------------------------------------------------------------------
	                update.set(event.kind().name(),child);  ////////////////////------------------
	               ////////////// System.out.format("%s: %s\n", event.kind().name(), child);
	                
	                // if directory is created, and watching recursively, then
	                // register it and its sub-directories
	                if (recursive && (kind == ENTRY_CREATE)) {
	                    try {
	                        if (Attributes.readBasicFileAttributes(child, NOFOLLOW_LINKS).isDirectory()) {
	                            registerAll(child);
	                        }
	                    } catch (IOException x) {
	                        // ignore to keep sample readbale
	                    }
	                }
	            }
	            // reset key and remove from set if directory no longer accessible
	            boolean valid = key.reset();
	            if (!valid) {
	                keys.remove(key);
	                // all directories are inaccessible
	                if (keys.isEmpty()) {
	                    break;
	                }
	            }
	        }
	        
	    }
	    
	    void usage() {
	        System.err.println("usage: java WatchDir [-r] dir");
	        System.exit(-1);
	    }
	}///end whachdir
	
	private String[] sourceList;
	ArrayList<Algo>  algoList;
	MenuTag_File dataBase;
	MenuDirects dirDB;
	private WatchDir x;
    public ObserverThread(String[] sourceList, ArrayList<Algo>   algo,MenuTag_File dataBase,MenuDirects dirDB){
    	
//	       this.sourceList=sourceList;
       this.sourceList=sourceList;//dirDB.retAllDir();
       this.dataBase=dataBase;
       algoList=algo;
       this.dirDB=dirDB;
    }  
  
    public void run() {   	
    	if (sourceList.length>0){
    			int i=0;
    			String temp=sourceList[0];
    			temp=temp.replaceAll("////////", "////");
    			//Path dir = Paths.get(sourceList[0]);
    			Path dir = Paths.get(temp);
    			try {
    				//////////////////<-----------------------------------------
					x= new WatchDir(dir, true);
					for (i=1; i<sourceList.length; i++){
	    				dir = Paths.get(sourceList[i]);
							x.register(dir);
	    			}
	    			x.processEvents();
				} catch (IOException e) {
    				 e.printStackTrace(System.err);
    			}
    			// dont need to add files or folders that not exist
	    		}
        }
}